package net.zoneland.knowledge.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.zoneland.knowledge.constant.DocumentVerificationConstants;
import net.zoneland.knowledge.controller.bean.DocumentVerificationParams;
import net.zoneland.knowledge.controller.bean.TermInfo;
import net.zoneland.knowledge.controller.bean.TermInfoParams;
import net.zoneland.knowledge.enums.TermTypeEnum;
import net.zoneland.knowledge.exceptions.DocumentVerificationException;
import net.zoneland.knowledge.model.DocumentProcessedContent;
import net.zoneland.knowledge.model.ResponseResult;
import net.zoneland.knowledge.model.VerifyResponseResult;
import net.zoneland.knowledge.model.VerifyResultModel;
import net.zoneland.knowledge.service.DocumentVerificationService;
import net.zoneland.knowledge.service.TermsInfoManagementService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 文稿校验.
 */
@RestController
@RequestMapping("/documentVerification")
public class DocumentVerificationController extends AbstractBaseController {

    /**
     * 日志.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(DocumentVerificationController.class);

    /**
     * 校验文稿服务接口.
     */
    @Resource
    private DocumentVerificationService documentVerificationService;

    /**
     * 名词管理服务接口.
     */
    @Resource
    private TermsInfoManagementService termsInfoManagementService;

    /**
     * json工具类.
     */
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 文稿校验.
     * @param request 请求.
     * @param content 要校验的内容，json格式.
     * @return 返回校验结果 "result": 成功"1",失败"0",message:"",resTime：请求时间，data：校验结果详细信息.
     */
    @PostMapping("/verifyDocument")
    public ResponseResult verifyDocument(final HttpServletRequest request, @RequestBody final String content) {
        final ResponseResult res = new ResponseResult();
        try {
            getAndCheckCurrentUser(request);
            //构建文稿校验请求参数.
            final DocumentVerificationParams contentInfo = objectMapper.readValue(content, DocumentVerificationParams.class);
            //校验文稿,这里可能校验失败抛出DocumentVerificationException
            final String response = documentVerificationService.verifyDocument(contentInfo);
            //当校验文稿返回不为空，开始解析结果对象返回
            if(StringUtils.isNotBlank(response)) {
                //将校验文稿返回的内容转成对象.
                final VerifyResponseResult verifyResponseResult = objectMapper.readValue(response, VerifyResponseResult.class);
                //判断解析结果，空了就不进行详情解析和塞值
                if(Objects.nonNull(verifyResponseResult)) {
                    //对文稿校验结果详细信息进行解析，并塞到data里面返回
                    res.setData(objectMapper.writeValueAsString(coverResponseContentToObject(verifyResponseResult)));
                }
            }
        } catch (final DocumentVerificationException | JsonProcessingException e) {
            LOGGER.error(DocumentVerificationConstants.VERIFY_DOCUMENT_ERROR + "document is [{}]", content, e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage(DocumentVerificationConstants.VERIFY_DOCUMENT_ERROR + "document is [" + content + "]" +e.getMessage());
        }
        return res;
    }


    /**
     * 同步词库.
     * @return 返回请求同步词库接口成功还是失败 "result": 成功"1",失败"0",message:"",resTime：请求时间，data：更新词库结果 是成功还是失败，以及详细信息.
     */
    @PostMapping("/syncTerms")
    public ResponseResult syncTerms(final HttpServletRequest request) {
        final ResponseResult res = new ResponseResult();
        try {
            getAndCheckCurrentUser(request);

            //查询名词词库列表信息
            final List<TermInfo> termInfoList = termsInfoManagementService.queryTermsLike(new TermInfo());
            //构建同步词库的请求参数.
            final TermInfoParams termInfoParams = new TermInfoParams();
            //专用词special_word[]
            termInfoParams.setSpecial_word(buildSpecialWord(termInfoList));
            //敏感词sensitive_word{"aa":["ab","bb"],...}，
            termInfoParams.setSensitive_word(buildSensitiveWord(termInfoList));
            //必须有一个有值，否则不发起请求，返回专用词和敏感词查询都为空异常
            Assert.isTrue(
                    !((null == termInfoParams.getSpecial_word() || termInfoParams.getSpecial_word().length == 0)
                    && termInfoParams.getSensitive_word().isEmpty()) ,
                    "敏感词和专用词不能都为空！！");
            res.setData(documentVerificationService.updateTerms(termInfoParams));
        } catch (final Exception e) {
            LOGGER.error(DocumentVerificationConstants.SYNC_TERMS_ERROR, e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage(DocumentVerificationConstants.SYNC_TERMS_ERROR + e.getMessage());
        }
        return res;
    }

    /**
     * 构建特殊词数组.
     * @param termInfoList 名词词库List
     * @return 返回特殊词数组列表
     */
    private String[] buildSpecialWord(final List<TermInfo> termInfoList) {
        //1、从名词信息列表termInfoList里面获取类型为专用词的数据
        //2、获取名词TermName，组成参数special_word:String[]{TermNameList}
        return termInfoList.stream()
                .filter(Objects::nonNull)
                .filter(termInfo -> termInfo.getTermType().equals(TermTypeEnum.SPECIAL_WORD))
                .map(TermInfo::getTermName)
                .toArray(String[]::new);
    }

    /**
     * 构建敏感词.
     * @param termInfoList 名词词库List
     * @return 返回敏感词Map<String(名词), String(以逗号分隔的替换词字符串)>
     */
    private Map<String, String> buildSensitiveWord(final List<TermInfo> termInfoList) {
        //1、从名词信息列表termInfoList里面获取类型为敏感词的数据
        //2、获取名词TermName作为key ，以及对应的替换词SubstituteWord，组成参数sensitive_word:Map<String, SubstituteWordString(以逗号分隔的替换词字符串)>
        return termInfoList.stream()
                .filter(Objects::nonNull)
                .filter(term -> term.getTermType().equals(TermTypeEnum.SENSITIVE_WORD))
                .collect(Collectors.toMap(TermInfo::getTermName, TermInfo::getSubstituteWord, (a, b) -> b));
    }


    /**
     * 将请求返回的校验结果list<map>解析成list<object>并塞到要返回的前端对象里面.
     * @param verifyResponseResult 校验结果
     * @return 内容为list的结果对象
     */
    private VerifyResultModel coverResponseContentToObject(final VerifyResponseResult verifyResponseResult) {
        //将结果解析成要返回给前端的对象
        final VerifyResultModel verifyResultModel = buildVerifyResultModel(verifyResponseResult);
        //如果返回成功，才进行内容解析
        if(verifyResponseResult.getCode() != DocumentVerificationConstants.VERIFY_DOCUMENT_SUCCESS) {
            return verifyResultModel;
        }

        //获取要解析的校验结果内容并过滤是不是都有值
        final List<Map<String, DocumentProcessedContent>> contentList = verifyResponseResult.getProcessed_content()
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        //把map解析成object返回
        verifyResultModel.setDocumentProcessedContentList(coverContentMapToObject(contentList));
        return verifyResultModel;
    }

    /**
     * 将校验详情内容List<map<String, Object>>转成对象list<Object>.
     * @param processedContent 校验返回详情内容.
     * @return 详情内容对象的列表.
     */
    private List<DocumentProcessedContent> coverContentMapToObject(final List<Map<String, DocumentProcessedContent>> processedContent) {
        // 校验结果对象的list,先扁平化成Map<String, DocumentProcessedContent>
        return processedContent.stream()
                .map(Map::entrySet)
                .flatMap(Collection::stream)
                .map(this::setContentCoordinateAndReturn)
                .filter(Objects::nonNull)
                //当数据类型为专有词的时候去掉不要
                .filter(content -> !StringUtils.equals(DocumentVerificationConstants.PROCESSED_CONTENT_TYPE, content.getType()))
                .collect(Collectors.toList());
    }

    /**
     * 将坐标信息设置到详情内容对象DocumentProcessedContent并返回
     * @param entry 详情内容
     * @return 包含坐标信息的详情内容
     */
    private DocumentProcessedContent setContentCoordinateAndReturn(final Map.Entry<String, DocumentProcessedContent> entry) {
        try {
            // 获取字词的坐标，办公宝接口返回的是一个数组，[row_num, col_start_num, index_num]
            // 第1位row_num段落，第2位col_start_num是列数开始位置,第3位Index_num是结束位置
            // 如[1,34,36],代表的是第1段(以换行符分段),第34-36个字符，都是从0开始
            final int[] coordinateArray = objectMapper.readValue(entry.getKey(), int[].class);
            // 将坐标信息合并到校验结果对象里DocumentProcessedContent
            final DocumentProcessedContent content = entry.getValue();
            // 办公宝接口返回的是一个数组[段落, 字词开始位置, 字词结束位置]，这里只需要段落和字词结束列数位置
            // 设置字词段落
            content.setRow_num(coordinateArray[DocumentVerificationConstants.WORD_ROW_NUM_INDEX]);
            // 字词结束位置
            content.setIndex_num(coordinateArray[DocumentVerificationConstants.WORD_INDEX_NUM_INDEX]);
            return content;
        } catch (final JsonProcessingException jsonProcessingException) {
            LOGGER.error("coverResponseContentToObject cover key(坐标：{})  error, json processing exception", entry.getKey(), jsonProcessingException);
            return null;
        }
    }

    /**
     * 构建文稿校验结果.
     * @param verifyResponseResult 校验文稿的结果.
     * @return 返回校验结果对象.
     */
    private VerifyResultModel buildVerifyResultModel(final VerifyResponseResult verifyResponseResult) {
        //将结果解析成要返回给前端的对象
        final VerifyResultModel verifyResultModel = new VerifyResultModel();
        verifyResultModel.setCode(verifyResponseResult.getCode());
        verifyResultModel.setMsg(verifyResponseResult.getMsg());
        return verifyResultModel;
    }

}
